package com.wang.sqlsession;

import com.wang.config.BoundSql;
import com.wang.pojo.Configuration;
import com.wang.pojo.MappedStatement;
import com.wang.utils.GenericTokenParser;
import com.wang.utils.ParameterMapping;
import com.wang.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by wangq on 2021/7/1.
 */
public class SimpleExecutor implements Executor {
    @Override
    public void create(Configuration configuration, MappedStatement mappedStatement, Object... objects) throws Exception {
        PreparedStatement preparedStatement = getPreparedStatement(configuration, mappedStatement, objects);
        preparedStatement.executeUpdate();
    }

    @Override
    public void update(Configuration configuration, MappedStatement mappedStatement, Object... objects) throws Exception {
        PreparedStatement preparedStatement = getPreparedStatement(configuration, mappedStatement, objects);
        preparedStatement.executeUpdate();
    }

    @Override
    public void delete(Configuration configuration, MappedStatement mappedStatement, Object... objects) throws Exception {
        PreparedStatement preparedStatement = getPreparedStatement(configuration, mappedStatement, objects);
        preparedStatement.executeUpdate();
    }

    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... objects) throws Exception {
        // 获取预处理对象，并设置参数
        PreparedStatement preparedStatement = getPreparedStatement(configuration, mappedStatement, objects);

        // 5、查询结果集
        ResultSet resultSet = preparedStatement.executeQuery();

        // 6、按照查询结果的值，封装返回结果集
        Class<?> resultTypeClass = getCLassType(mappedStatement.getResultType());

        List<E> resultList = new ArrayList<>();
        while (resultSet.next()) {
            Object resultTypeInstance = resultTypeClass.newInstance(); // 每列数据对应一个实体
            // 获取从数据中查询出来的数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i < metaData.getColumnCount() + 1; i++) {
                // 获取查询结果的数据库对应的字段名
                String columnName = metaData.getColumnName(i);
                // 通过数据库字段名获取该字段的值
                Object columnValue = resultSet.getObject(columnName);
                // 使用反射或者内省，根据数据库字段和返回实体类的变量对应关系，封装返回结果
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass); // 获取列名称和参数类实体的对应属性描述器
                Method writeMethod = propertyDescriptor.getWriteMethod(); // 获取属性描述器的写方法
                writeMethod.invoke(resultTypeInstance, columnValue); // 把列的值写到返回实体中
            }
            resultList.add((E) resultTypeInstance);
        }
        return resultList;
    }

    /**
     * 获取预处理对象，并设置参数
     *
     * @param configuration   数据库配置文件
     * @param mappedStatement sql解析对象
     * @param objects         参数对象
     * @return 预处理对象
     */
    public PreparedStatement getPreparedStatement(Configuration configuration, MappedStatement mappedStatement, Object... objects) throws Exception {
        // 1、注册驱动，获取连接
        Connection connection = configuration.getDataSource().getConnection();

        // 2、获取sql语句，并转换sql语句，把#{}占位符转换为？转换时对#{}中的值进行解析
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);

        // 3、获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getParseSql());

        // 4、 设置参数
        Class<?> parameterTypeClass = getCLassType(mappedStatement.getParameterType()); // 获取参数对象的class

        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String content = parameterMapping.getContent();
            // 通过反射获取参数对象中的字段
            Field declaredField = parameterTypeClass.getDeclaredField(content);
            declaredField.setAccessible(true); // 设置暴力访问，防止字段私有无法访问
            Object paramObject = declaredField.get(objects[0]); // 获取参数对象中，该字段的值
            preparedStatement.setObject(i + 1, paramObject);
        }
        return preparedStatement;
    }

    private Class<?> getCLassType(String classType) throws ClassNotFoundException {
        return classType != null ? Class.forName(classType) : null;
    }

    /**
     * 解析原始sql语句中的#{}，把#{}使用？代替，并将#{}中的值解析出来并存储
     *
     * @param sql 原始sql语句
     * @return 解析后的sql语句
     */
    private BoundSql getBoundSql(String sql) {
        // 配置标记解析器，并对sql语句占位符进行解析
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        String parseSql = genericTokenParser.parse(sql); // 解析后的sql语句
        List<ParameterMapping> parameterMappingList = parameterMappingTokenHandler.getParameterMappings(); // 解析sql语句中的占位符字段集合
        return new BoundSql(parseSql, parameterMappingList);
    }


}
